package com.sping.gobang.common.api;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sping.gobang.common.constant.Constants;
import com.sping.gobang.game.OnlineUserManager;
import com.sping.gobang.game.Room;
import com.sping.gobang.game.RoomManager;
import com.sping.gobang.mapper.UserMapper;
import com.sping.gobang.pojo.datapbject.User;
import com.sping.gobang.pojo.response.GameReadyResponse;
import com.sping.gobang.pojo.response.GameResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.Map;

@Component
@Slf4j
public class GameAPI extends TextWebSocketHandler {//注意处理游戏房间相关
    @Autowired
    private OnlineUserManager onlineUserManager;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private RoomManager roomManager;
    @Autowired
    private UserMapper userMapper;


    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse gameReadyResponse=new GameReadyResponse();
        //1.先获取用户的身份信息（从HttpSession里拿到用户当前的对象）
        User user = (User) session.getAttributes().get(Constants.SESSION_USER_KEY);
        //其实严格来说，我这里是做了统一拦截的，相当于绝对不可能通过直接访问game_room.html来进入游戏房间的
        //但是我前端没有加提示信息，这里为了严谨性，多加个验证
        if(user==null){
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("用户未登陆!");
            //服务器返回给客户端
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }
        //2.判定当前用户是否已经进入房间(拿着房间管理器进行查询）
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room==null){
            //如果位null，当前没有找到对应的房间，该玩家还没有匹配到
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("用户尚未匹配到!");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }

        //3.判断当前是否多开
        if(onlineUserManager.getFromGameHall(user.getUserId())!=null||onlineUserManager
                .getFromGameRoom(user.getUserId())!=null){
            //一个帐号，一边是在游戏大厅，一边是在游戏房间，这也算是多开
            gameReadyResponse.setOk(true);
            gameReadyResponse.setReason("禁止多开游戏页面");
            gameReadyResponse.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }
        //4.设置当前玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(),session);
      synchronized (room){
          //5.把两个玩家加入到游戏房间中
          //前面的创建房间/匹配过程，是在game_hall.html页面中完成的
          //在前面匹配到对手之后，需要经过页面跳转，来到game_room.html才算正式进入游戏房间（才算玩家准备就绪）
          //当前这个逻辑是在game_room.html页面加载的时候进行的
          //执行到当前逻辑，说明玩家已经页面跳转成功了
          if(room.getUser1()==null){
              //第一个玩家尚未加入房间
              //就把当前连接上wbesocket的玩家作为user1，加入到房间中
              room.setUser1(user);
              //把先连入房间的玩家作为先手方
              room.setWhiteUser(user.getUserId());
              log.info("玩家"+user.getUsername()+"已经准备就绪! 作为玩家1");
              return;
          }
          if(room.getUser2()==null){
              //如果进入到这个逻辑，说明玩家1已经加入房间了，现在要给当前玩家作为玩家2了
              room.setUser2(user);
              log.info("玩家"+user.getUsername()+"已经准备就绪! 作为玩家2");
              //当两个玩家都加入成功之后，就要让服务器，给这两个玩家返回websocket的响应数据
              //通知两个玩家说，游戏双方都已经准备好了
              //通知玩家1
              noticeGameReady(room,room.getUser1(),room.getUser2());
              //通知玩家2
              noticeGameReady(room,room.getUser2(),room.getUser1());
              return;
          }
      }
        //6.此处如果又有玩家尝试连接同一个房间，就提示报错
        //理论情况下不会发生这种情况，但是为了使得程序更加健壮，加一个判定和提示
        gameReadyResponse.setOk(false);
        gameReadyResponse.setReason("当前房间已满，您不能加入房间");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse gameReadyResponse=new GameReadyResponse();
        gameReadyResponse.setMessage("gameReady");
        gameReadyResponse.setOk(true);
        gameReadyResponse.setReason("");
        gameReadyResponse.setRoomId(room.getRoomId());
        gameReadyResponse.setThisUserId(thisUser.getUserId());
        gameReadyResponse.setThatUserId(thatUser.getUserId());
        gameReadyResponse.setWhiteUser(room.getWhiteUser());
        WebSocketSession fromGameRoom = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        fromGameRoom.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));

    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        User user = (User) session.getAttributes().get(Constants.SESSION_USER_KEY);
        //其实严格来说，我这里是做了统一拦截的，相当于绝对不可能通过直接访问game_room.html来进入游戏房间的
        //但是我前端没有加提示信息，这里为了严谨性，多加个验证
        if(user==null){
            //好进行区分
            log.info("[handleTextMessage]当前玩家尚未登陆!");
            return;
            
        }
        //根据玩家id获取到房间对象
        Room room = roomManager.getRoomByUserId(user.getUserId());
        //通过room对象来处理这次具体的请求
        room.putChess(message.getPayload());


    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get(Constants.SESSION_USER_KEY);
        //其实严格来说，我这里是做了统一拦截的，相当于绝对不可能通过直接访问game_room.html来进入游戏房间的
        //但是我前端没有加提示信息，这里为了严谨性，多加个验证
        if(user==null){
            return;
        }
        WebSocketSession fromGameRoom = onlineUserManager.getFromGameRoom(user.getUserId());
        if(session==fromGameRoom){
            //是为了避免在多开的环境下，第二个用户退出连接动作，导致第一个用户的会话被删除
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        log.info("当前用户"+user.getUsername()+"游戏房间连接异常!");

        //自己的连接已关闭，通知对手已获胜
        noticeThatUserWin(user);

    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get(Constants.SESSION_USER_KEY);
        //其实严格来说，我这里是做了统一拦截的，相当于绝对不可能通过直接访问game_room.html来进入游戏房间的
        //但是我前端没有加提示信息，这里为了严谨性，多加个验证
        if(user==null){
            //此处简单处理，在断开连接的时候就不给客户端返回响应了
            return;
        }
        WebSocketSession fromGameRoom = onlineUserManager.getFromGameRoom(user.getUserId());
        if(session==fromGameRoom){
            //是为了避免在多开的环境下，第二个用户退出连接动作，导致第一个用户的会话被删除
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        log.info("当前用户"+user.getUsername()+"离开游戏房间!");
        //自己的连接已关闭，通知对手已获胜
        noticeThatUserWin(user);



    }

    private void noticeThatUserWin(User user) throws IOException {
        //1.根据当前玩家，找到该玩家所在的房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room==null){
            //房间已经被释放，也就没有"对手"了
            log.info("当前房间已经被释放,无需通知对手!");
            return;
        }
        //2.根据房间找到对手
        //若这个user是user1，则对手是user2
        User thatUser=(user==room.getUser1())?room.getUser2():room.getUser1();
//        if(thatUser==null){
//            log.info("对手信息不存在,无需通知!");
//            //清理房间资源
//            roomManager.remove(room.getRoomId(), user.getUserId());
//            return;
//        }
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if(webSocketSession==null){
            //这就说明对手也掉线了
            log.info("对手也已经掉线,无需通知!");
            return;
        }
        //3.构造一个响应，来通知对手，你是获胜方
        GameResponse gameResponse=new GameResponse();
        gameResponse.setMessage("putChess");
        gameResponse.setUserId(thatUser.getUserId());
        gameResponse.setWinner(thatUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameResponse)));

        //4.更新玩家的分数信息
        int winUserId=thatUser.getUserId();
        int loseUserId=user.getUserId();
        userMapper.userWin(winUserId);
        userMapper.userLose(loseUserId);

        //5.释放房间对象
        roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());


    }
}
